/**
 * Copyright 2018-2019 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.modulelink.util.vfs;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.jianggujin.modulelink.util.JLogFactory;
import com.jianggujin.modulelink.util.JLogFactory.JLog;

/**
 * 提供一个非常简单的API，用于访问应用程序服务器内的资源
 * 
 * @author jianggujin
 *
 */
public abstract class JVFS {

    private static final JLog logger = JLogFactory.getLog(JVFS.class);

    /**
     * 用户通过{@link #addImplClass(Class)}添加的自定义实现
     */
    public static final List<Class<? extends JVFS>> USER_IMPLEMENTATIONS = new ArrayList<Class<? extends JVFS>>();

    private static class JVFSHolder {
        static final JVFS INSTANCE = createVFS();

        static JVFS createVFS() {
            // 尝试优先使用用户添加的实现
            List<Class<? extends JVFS>> impls = new ArrayList<Class<? extends JVFS>>();
            impls.addAll(USER_IMPLEMENTATIONS);
            impls.add(JJBoss6VFS.class);
            impls.add(JDefaultVFS.class);

            // 尝试遍历所有的实现直至找到一个可用的实现为止
            JVFS vfs = null;
            for (int i = 0; vfs == null || !vfs.isValid(); i++) {
                Class<? extends JVFS> impl = impls.get(i);
                try {
                    vfs = impl.newInstance();
                    if (vfs == null || !vfs.isValid()) {
                        if (logger.isDebugEnabled()) {
                            logger.debug(
                                    "JVFS implementation " + impl.getName() + " is not valid in this environment.");
                        }
                    }
                } catch (InstantiationException e) {
                    logger.error("Failed to instantiate " + impl, e);
                    return null;
                } catch (IllegalAccessException e) {
                    logger.error("Failed to instantiate " + impl, e);
                    return null;
                }
            }

            if (logger.isDebugEnabled()) {
                logger.debug("Using JVFS adapter " + vfs.getClass().getName());
            }
            return vfs;
        }
    }

    /**
     * 获得实例
     * 
     * @return {@link JVFS}实例
     */
    public static JVFS getInstance() {
        return JVFSHolder.INSTANCE;
    }

    /**
     * 添加一个{@link JVFS}实现，该实现会尝试在内建实现之前最先使用
     * 
     * @param clazz {@link JVFS}实现类
     */
    public static void addImplClass(Class<? extends JVFS> clazz) {
        if (clazz != null) {
            USER_IMPLEMENTATIONS.add(clazz);
        }
    }

    /**
     * 通过名称获得类，如果未找到则返回null
     * 
     * @param className 类名称
     * @return 类名称对应的类对象
     */
    protected static Class<?> getClass(String className) {
        try {
            return Thread.currentThread().getContextClassLoader().loadClass(className);
        } catch (ClassNotFoundException e) {
            if (logger.isDebugEnabled()) {
                logger.debug("Class not found: " + className);
            }
            return null;
        }
    }

    /**
     * 通过方法名和参数类型获得{@link Method}对象，如果未找到则返回null
     * 
     * @param clazz          需要查找的类
     * @param methodName     方法名称
     * @param parameterTypes 参数类型
     * @return 查找的方法，如果未找到则返回null
     */
    protected static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
        if (clazz == null) {
            return null;
        }
        try {
            return clazz.getMethod(methodName, parameterTypes);
        } catch (SecurityException e) {
            logger.error(
                    "Security exception looking for method " + clazz.getName() + "." + methodName + ".  Cause: " + e);
            return null;
        } catch (NoSuchMethodException e) {
            logger.error(
                    "Method not found " + clazz.getName() + "." + methodName + "." + methodName + ".  Cause: " + e);
            return null;
        }
    }

    /**
     * 执行指定的{@link Method}，并且返回执行后的结果
     * 
     * @param <T>        执行结果泛型参数
     * @param method     需要执行的方法
     * @param object     方法所关联的对象
     * @param parameters 方法执行参数
     * @return 方法执行结果
     * @throws IOException      IO异常
     * @throws RuntimeException 运行时异常
     */
    protected static <T> T invoke(Method method, Object object, Object... parameters)
            throws IOException, RuntimeException {
        try {
            @SuppressWarnings("unchecked")
            T result = (T) method.invoke(object, parameters);
            return result;
        } catch (IllegalArgumentException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            if (e.getTargetException() instanceof IOException) {
                throw (IOException) e.getTargetException();
            } else {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 获得指定路径资源的URL集合
     * 
     * @param path 资源路径
     * @return 指定路径资源的URL集合
     * @throws IOException IO异常
     */
    protected static List<URL> getResources(String path) throws IOException {
        return Collections.list(Thread.currentThread().getContextClassLoader().getResources(path));
    }

    /**
     * 如果在当前环境中，{@link JVFS}的实现是可用的，则返回true
     * 
     * @return 实现可用则返回true
     */
    public abstract boolean isValid();

    /**
     * 递归列出由URL标识的资源的子资源的所有资源路径
     * 
     * @param url     需要递归的URL
     * @param forPath 路径
     * @return 子资源路径
     * @throws IOException IO异常
     */
    protected abstract List<String> list(URL url, String forPath) throws IOException;

    /**
     * 递归列出在指定路径中找到的所有资源的子资源的所有资源路径
     * 
     * @param path 路径
     * @return 子资源路径
     * @throws IOException IO异常
     */
    public List<String> list(String path) throws IOException {
        List<String> names = new ArrayList<String>();
        for (URL url : getResources(path)) {
            names.addAll(list(url, path));
        }
        return names;
    }
}
